package com.pingan.haofang.searchcloud.solr.api;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.util.NamedList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.pingan.haofang.searchcloud.api.IndexRowData;
import com.pingan.haofang.searchcloud.api.exception.SearchException;
import com.pingan.haofang.searchcloud.api.query.parser.dbobject.SqlNode;
import com.pingan.haofang.searchcloud.api.query.parser.dbobject.sqlnode.ColumnNode;
import com.pingan.haofang.searchcloud.api.query.parser.dbobject.sqlnode.aggregate.AggregateFunction;
import com.pingan.haofang.searchcloud.api.query.parser.dbobject.sqlnode.aggregate.Count;
import com.pingan.haofang.searchcloud.api.query.parser.dbobject.util.SqlNodeUtil;
import com.pingan.haofang.searchcloud.api.query.parser.element.QueryBlock;
import com.pingan.haofang.searchcloud.api.query.parser.element.SelectStatement;
import com.pingan.haofang.searchcloud.solr.api.client.SolrClientFactory;
import com.pingan.haofang.searchcloud.solr.constants.SolrConstant;

/**
 * SOLR查询服务
 *
 * @author LUYI374
 * @date 2017年7月5日
 * @since 1.0.0
 */
@Component
public class SolrQueryService {

    static Logger LOG = LoggerFactory.getLogger(SolrQueryService.class);

    @Autowired
    private SolrClientFactory solrClientFactory;

    /**
     * 执行SOLR查询
     *
     * @param statement
     * @param collection
     * @param host
     * @return
     */
    @SuppressWarnings("unchecked")
    public SolrQueryResult query(SelectStatement statement, String collection, String host) {

        SolrQueryStatement solrStatement = SolrQueryBuilder.build(statement);
        LOG.info("solr query: [{} {}]", collection, solrStatement.getSolrQuery().toString());

        QueryResponse queryResponse = null;
        try {
            queryResponse = solrClientFactory.getByHost(host).query(collection, solrStatement.getSolrQuery(),
                    SolrRequest.METHOD.POST);
        } catch (SolrServerException | IOException e) {
            throw new SearchException("execute solr query failed, please check", e);
        }

        QueryBlock queryBlock = statement.getQueryBlock();
        SolrDocumentList solrDocumentList = queryResponse.getResults();

        SolrQueryResult result = new SolrQueryResult();
        result.setTime(queryResponse.getQTime());
        result.setTotalFound(solrDocumentList.getNumFound());

        List<IndexRowData> datas = null;

        if (queryBlock.useAggregate()) {

            NamedList<Object> facetMap = (NamedList<Object>) queryResponse.getResponse().get("facets");
            if (facetMap != null) {
                
                Object childNode = facetMap.getVal(facetMap.size() - 1);

                if (childNode instanceof NamedList) {
                    LinkedHashMap<String, IndexRowData> dataMap = new LinkedHashMap<String, IndexRowData>();

                    int i = facetMap.size();
                    while (i > 0) {
                        if (!(facetMap.getVal(facetMap.size() - i) instanceof NamedList)) {
                            i--;
                            continue;
                        }
                        resolveAggPivot("", new LinkedList<IndexRowData>(), dataMap, facetMap,
                                statement.getQueryBlock().getAggregateClause(), queryBlock.getSelectColumnMap(), "", i);

                        // 数据合并，字段排序
                        datas = new ArrayList<>(dataMap.size());
                        for (IndexRowData data : dataMap.values()) {
                            IndexRowData newData = new IndexRowData();

                            for (SqlNode selectNode : queryBlock.getSelectClause()) {
                                if (SqlNodeUtil.isColumn(selectNode)) {
                                    if (solrStatement.getSelectItemMapping()
                                            .containsKey(((ColumnNode) selectNode).getColumnName())) {
                                        for (String item : solrStatement.getSelectItemMapping()
                                                .get(((ColumnNode) selectNode).getColumnName())) {
                                            Object value = data.get(item);
                                            if (value != null) {
                                                newData.put(selectNode.getAlias(), value);
                                            }
                                        }
                                    } else {
                                        newData.put(selectNode.getAlias(),
                                                data.get(((ColumnNode) selectNode).getColumnName()));
                                    }
                                } else {
                                    newData.put(selectNode.getAlias(), data.get(selectNode.getAlias()));
                                }
                            }

                            datas.add(newData);
                        }
                        i--;
                    }
                } else {

                    Map<Integer, AggregateFunction> aggMap = statement.getQueryBlock().getAggregateClause();
                    if (aggMap != null) {
                        datas = new ArrayList<>(1);
                        IndexRowData data = new IndexRowData();
                        resolveAggData(aggMap, facetMap, data);

                        IndexRowData newData = new IndexRowData();

                        for (SqlNode selectNode : queryBlock.getSelectClause()) {
                            newData.put(selectNode.getAlias(), data.get(selectNode.getAlias()));
                        }
                        datas.add(newData);
                    } else {
                        datas = new ArrayList<>(0);
                    }
                }

            }
        } else {
            // 针对非聚合查询的处理
            datas = new ArrayList<IndexRowData>(solrDocumentList.size());
            for (int i = 0; i < solrDocumentList.size(); i++) {
                SolrDocument solrDocument = solrDocumentList.get(i);

                IndexRowData data = new IndexRowData();
                for (ColumnNode node : queryBlock.getSelectColumnMap().values()) {
                    data.put(node.getAlias(), solrDocument.getFieldValue(node.getColumnName()));
                }

                datas.add(data);
            }
        }

        if (datas == null) {
            result.setResult(new ArrayList<>(0));
            result.setResultSize(0L);
        } else {
            result.setResult(datas);
            result.setResultSize(datas.size());
        }

        return result;
    }

    /**
     * solr原生查询
     *
     * @param query
     * @param collection
     * @param host
     * @return
     */
    public SolrQueryResult query(SolrQuery query, String collection, String host) {
        LOG.info("solr query: [{} {}]", collection, query.toString());

        QueryResponse queryResponse = null;
        try {
            queryResponse = solrClientFactory.getByHost(host).query(collection, query, SolrRequest.METHOD.POST);
        } catch (SolrServerException | IOException e) {
            throw new SearchException("execute solr query failed, please check", e);
        }
        SolrQueryResult result = new SolrQueryResult();
        SolrDocumentList solrDocumentList = queryResponse.getResults();
        result.setTime(queryResponse.getQTime());
        result.setTotalFound(solrDocumentList.getNumFound());

        List<IndexRowData> datas = new ArrayList<>(solrDocumentList.size());
        for (int i = 0; i < solrDocumentList.size(); i++) {
            SolrDocument solrDocument = solrDocumentList.get(i);

            IndexRowData data = new IndexRowData();
            for (String key : solrDocument.keySet()) {
                data.put(key, solrDocument.get(key));
            }

            datas.add(data);
        }
        result.setResult(datas);
        result.setResultSize(datas.size());
        result.setHighlighting(queryResponse.getHighlighting());
        return result;
    }

    /**
     * 解析pivot，这规则就是一坨shit（shit中的航空母舰）
     *
     * @param key
     * @param stack
     * @param dataMap
     * @param node
     * @param aggMap
     * @param selectColumnMap
     */
    @SuppressWarnings("unchecked")
    private void resolveAggPivot(String key, LinkedList<IndexRowData> stack, Map<String, IndexRowData> dataMap,
            NamedList<Object> node, Map<Integer, AggregateFunction> aggMap, Map<String, ColumnNode> selectColumnMap,
            String fieldName, int outerRightIndex) {

        boolean hasVal = false;
        String firstNodeName = node.getName(0);
        Object val = null;
        if ("val".equals(firstNodeName)) {
            hasVal = true;
            val = node.get("val");
        }
        List<NamedList<Object>> buckets = (List<NamedList<Object>>) node.get("buckets");
        NamedList<Object> missing = (NamedList<Object>) node.get("missing");

        // 如果为空，则认为是一个虚拟节点，需要进一步执行递归
        if (!hasVal) {

            if (buckets == null && missing == null) {
                int len = node.size();
                
                String currentFieldName = node.getName(len - outerRightIndex);
                Object fieldVal = node.getVal(len - outerRightIndex);

                if (fieldVal != null && fieldVal instanceof NamedList) {
                    NamedList<Object> fieldNode = (NamedList<Object>) fieldVal;
                    resolveAggPivot(key, stack, dataMap, fieldNode, aggMap, selectColumnMap, currentFieldName,
                            outerRightIndex);
                }
            } else {

                boolean hasContainNullBucket = false;
                // 针对bucket的处理
                if (buckets != null) {
                    if (!buckets.isEmpty()) {
                        for (NamedList<Object> bucket : buckets) {
                            resolveAggPivot(key, stack, dataMap, bucket, aggMap, selectColumnMap, fieldName,
                                    outerRightIndex);
                        }
                    } else {
                        NamedList<Object> nullNamedList = new NamedList<Object>(1);
                        nullNamedList.add("val", null);
                        resolveAggPivot(key, stack, dataMap, nullNamedList, aggMap, selectColumnMap, fieldName,
                                outerRightIndex);
                        hasContainNullBucket = true;
                    }
                }

                // 针对missing的处理
                if (missing != null) {
//                    Integer count = (Integer) missing.get("count");//throw ClassCastException
                    Long count = Optional.ofNullable(missing.get("count")).map(cunt ->((Number)cunt).longValue()).orElse(0L);
                    if (count > 0) {
                        if (hasContainNullBucket) {
                            resolveAggPivot(key + ",null", stack, dataMap, missing, aggMap, selectColumnMap, fieldName,
                                    outerRightIndex);
                        } else {
                            NamedList<Object> nullNamedList = new NamedList<Object>(1);
                            nullNamedList.add("val", null);
                            resolveAggPivot(key, stack, dataMap, nullNamedList, aggMap, selectColumnMap, fieldName,
                                    outerRightIndex);
                        }
                    }
                }
            }

        } else {
            String currentKey = key;
            currentKey += "," + val;

            IndexRowData data = new IndexRowData();
            if (!stack.isEmpty()) {
                data.putAll(stack.getFirst());
            }

            data.put(fieldName, val);

            int len = node.size();
            String currentFieldName = node.getName(len - 1);
            Object fieldVal = node.getVal(len - 1);

            if (fieldVal != null && fieldVal instanceof NamedList) {
                NamedList<Object> fieldNode = (NamedList<Object>) fieldVal;
                stack.push(data);
                resolveAggPivot(currentKey, stack, dataMap, fieldNode, aggMap, selectColumnMap, currentFieldName,
                        outerRightIndex);
                stack.pop();
            } else {
                stack.push(data);
                IndexRowData fullData = null;
                if (!dataMap.containsKey(currentKey)) {
                    fullData = stack.getFirst();
                    dataMap.put(currentKey, fullData);
                } else {
                    fullData = dataMap.get(currentKey);
                }

                // 处理聚合函数
                if (aggMap != null) {
                    resolveAggData(aggMap, node, fullData);
                }
                stack.pop();
            }
        }
    }


    /**
     * 解析并处理聚合函数
     *
     * @param aggMap
     * @param aggFuncMap
     * @param fullData
     */
    private void resolveAggData(Map<Integer, AggregateFunction> aggMap, NamedList<Object> aggFuncMap,
                                IndexRowData fullData) {
        for (int index : aggMap.keySet()) {
            AggregateFunction func = aggMap.get(index);

            Object val = null;
            if (func.getClass() == Count.class) {
                val = aggFuncMap.get("count");
            } else {
                val = aggFuncMap.get(SolrConstant.AGG_PREFIX + index);
            }

            fullData.put(func.getAlias(), val);
        }
    }

}
